/*
 *  MutaSim - an artificial life simulator based on cellular automata.
 *  Copyright (C) 2009-2010 Alexei Tereschenko <aytereschenko@gmail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tilex.sim;

import java.util.Random;


/**
 * Класс, описывающий симулируемый организм.
 */
public class Creature extends CreatureSample {

	@Override public String toString() {
		return "Creature [age=" + age + ", mass=" + mass + ", startMass=" + startMass + ", maxAge=" + maxAge
						+ ", reproductAge=" + reproductAge + ", childrenCount=" + childrenCount + ", color=" + color
						+ ", mutantColor=" + mutantColor + ", mutation=" + mutation + ", resourceEat=" + resourceEat
						+ ", resourceIn=" + resourceIn + ", resourceOut=" + resourceOut + "]";
	}

	private static Random	rnd				= new Random();

	private static int		resourceTypes	= 0;

	public static void setResourceTypes(int types) {
		resourceTypes = types;
	}

	/**
	 * Случайное число для равномерного распределения новорожденных по полю.
	 */
	private int	newBornSeed;

	public int getNewBornSeed() {
		return newBornSeed;
	}

	/**
	 * Кол-во уже рожденных потомков на текущем шаге.
	 */
	private int	born;

	/**
	 * Текущая масса.
	 */
	private int	mass;

	/**
	 * Текущий возраст.
	 */
	private int	age;

	public Creature() {}

	/**
	 * Инициализирует новый организм по шаблону.
	 * 
	 * @param sample
	 *            шаблон.
	 * @param mutate
	 *            надо ли мутировать.
	 * @param resourceTypes
	 *            кол-во видов ресурсов (для правильной мутации).
	 */
	public Creature(CreatureSample sample, boolean mutate) {
		color = sample.color;
		mutantColor = sample.mutantColor;
		startMass = sample.startMass;
		resourceEat = sample.resourceEat;
		resourceIn = sample.resourceIn;
		resourceOut = sample.resourceOut;
		reproductAge = sample.reproductAge;
		maxAge = sample.maxAge;
		childrenCount = sample.childrenCount;
		mutation = sample.mutation;

		if (mutate && mutation > rnd.nextInt(100)) {
			// выбираем случайный ген для мутации
			int gene = rnd.nextInt(8);
			// dg - изменение гена: -1/1
			int dg = rnd.nextInt(2);
			if (dg == 0) {
				dg = -1;
			}
			switch (gene) {
				case 0:
					startMass += dg;
					if (startMass < 1) {
						startMass = 1;
					}
					break;
				case 1:
					resourceEat += dg;
					if (resourceEat < 1) {
						resourceEat = 1;
					}
					break;
				case 2:
					resourceIn = rnd.nextInt(resourceTypes);
					break;
				case 3:
					resourceOut = rnd.nextInt(resourceTypes);
					break;
				case 4:
					reproductAge += dg;
					if (reproductAge < 2) {
						reproductAge = 2;
					}
					break;
				case 5:
					maxAge += dg;
					if (maxAge < 2) {
						maxAge = 2;
					}
					break;
				case 6:
					childrenCount += dg;
					if (childrenCount < 1) {
						childrenCount = 1;
					}
					break;
				case 7:
					mutation += dg;
					if (mutation < 0) {
						mutation = 0;
					}
					if (mutation > 100) {
						mutation = 100;
					}
					break;
			}
		}

		age = 0;
		mass = startMass;
		newBornSeed = rnd.nextInt(100);
	}

	public void setMass(int mass) {
		this.mass = mass;
	}

	public int getMass() {
		return mass;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getAge() {
		return age;
	}

	public boolean isDead() {
		return age > maxAge;
	}

	public boolean canReproduct() {
		return age >= reproductAge && mass > startMass && born < childrenCount;
	}

	/**
	 * Рождает одного потомка.
	 * 
	 * @return рожденный потомок
	 */
	public Creature reproduct() {
		Creature newBorn = new Creature(this, true);
		mass -= newBorn.mass;
		born++;
		return newBorn;
	}

	/**
	 * Выполняет симуляцию для организма.
	 * 
	 * @param resIn
	 *            кол-во ресурсов, имеющихся на клетке, в которой находится данный организм
	 * @return кол-во съеденных ресурсов
	 */
	public int simulate(int resIn) {
		int r = 0;
		// уже не новорожденный
		newBornSeed = -1;
		born = 0;
		// стареем
		age++;
		// если еще не умерли
		if (age <= maxAge) {
			// едим ресурсы
			r = Math.min(resourceEat, resIn);
			mass += r;
		}
		return r;
	}

}
